package com.ht.aems.fesmodel.persist;

import com.ht.aems.communicate.TcpPortInfo;
import com.ht.aems.communicate.UdpPortInfo;
import com.ht.aems.fesmodel.*;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 *
 * @author Dong Shufeng
 *         Date: 2007-3-26
 *         Time: 18:55:24
 */
public class FilePersistence implements FesPersistence {

    private static Logger log = Logger.getLogger(FilePersistence.class);

    File unitFile;

    File commInfoFile;

    Map<Integer, DefaultUnit> units = new HashMap<Integer, DefaultUnit>();
    Map<Integer, CommUnit> commUnits = new HashMap<Integer, CommUnit>();

    public FilePersistence() {
        this(true);
    }

    public FilePersistence(boolean isAppaned) {
        String filePath;
        if (System.getProperty("fes.persistence.filePath") != null)
            filePath = System.getProperty("fes.persistence.filePath");
        else filePath = System.getProperty("user.dir");
        File dir = new File(filePath);
        File dataDir = new File(dir.getPath() + File.separator + "fesdata");
        if (!dataDir.exists()) {
            if (!dataDir.mkdir())
                log.warn("can not create dir " + dataDir.getAbsolutePath());
        }
        unitFile = new File(dataDir.getPath() + File.separator + "fesunits");
        if (!unitFile.exists())
            try {
                if(!unitFile.createNewFile())
                    log.warn("Failed to create file:" + unitFile.getAbsoluteFile());
            } catch (IOException e) {
                log.warn("can not create file " + unitFile.getAbsoluteFile());
            }
        commInfoFile = new File(dataDir.getPath() + File.separator + "comminfos");
        if (!commInfoFile.exists())
            try {
                if(!commInfoFile.createNewFile())
                    log.warn("Failed to create file:" + commInfoFile.getAbsoluteFile());
            } catch (IOException e) {
                log.warn("can not create file " + commInfoFile.getAbsoluteFile());
            }
        if(isAppaned)
            readFiles();
    }

    private void readFiles() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(commInfoFile), "UTF8"));
            String str;
            while ((str = reader.readLine()) != null) {
                int nonEmpty = 0;
                while(str.charAt(nonEmpty) == ' ' || str.charAt(nonEmpty) == '\t')
                    nonEmpty++;
                if(str.charAt(nonEmpty) == '#')
                    continue;
                String[] content2 = str.split("\t");
                CommUnit comm = fillCommUnit(content2);
                if (comm.getId() > PersistenceUnit.getMaxId())
                    PersistenceUnit.setMaxId(comm.getId());
                commUnits.put(comm.getId(), comm);
            }
            reader.close();
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(unitFile), "UTF8"));
            while ((str = reader.readLine()) != null) {
                int nonEmpty = 0;
                while(str.charAt(nonEmpty) == ' ' || str.charAt(nonEmpty) == '\t')
                    nonEmpty++;
                if(str.charAt(nonEmpty) == '#')
                    continue;
                String[] content = str.split("\t");
                PersistenceUnit unit = new PersistenceUnit(Integer.parseInt(content[0]), content[2], Integer.parseInt(content[1]));
                if (unit.getId() > PersistenceUnit.getMaxId())
                    PersistenceUnit.setMaxId(unit.getId());
                units.put(unit.getId(), unit);
            }
            reader.close();
            fillUnitManager();
        } catch (IOException e) {
            log.warn(e);
        }
    }

    public boolean save() {
        StringBuilder buffer = new StringBuilder();
        buffer.append("#id\tparentId\tname\n");
        for(AbstractUnit unit : units.values())
            buffer.append(unit.getId()).append("\t").append(unit.getParentUnitId()).append("\t").append(unit.getName()).append("\n");
        try {
            Writer w = new OutputStreamWriter(new FileOutputStream(unitFile), "UTF8");
            w.write(buffer.toString());
            w.close();
        } catch (IOException e) {
            log.warn(e);
            return false;
        }
        buffer = new StringBuilder();
        buffer.append("#id\tparentId\tname\tyxNum\tyxBits\tycNum\telecNum\thost\tport\tcommType\tprotocol\n");
        for(CommUnit comm : commUnits.values()) {
            //nId,nDivisionId,sName,nYXPtNum,nYXBitNum,nYCPtNum,nDDPtNum, sHost, nPort, sCommType, sProtocol
            String values = comm.getId() + "\t" + comm.getParentUnitId() + "\t" + comm.getName() + "\t" +
                    comm.getDigitalPointNum() + "\t" + comm.getDigitalBitNum() + "\t" + comm.getAnalogNum() + "\t" + comm.getElectricNum();
            if (comm.getPortInfo() != null && comm.getPortInfo() instanceof TcpPortInfo) {
                String sHost = ((TcpPortInfo) comm.getPortInfo()).getRemoteIp();
                int port = ((TcpPortInfo) comm.getPortInfo()).getServerPort();
                //sHost,nPort,sCommType;
                values += ("\t" + sHost + "\t" + port + "\t" + comm.getPortInfo().getPortType() + "\t");
            } else if(comm.getPortInfo() != null && comm.getPortInfo() instanceof UdpPortInfo) {
                String sHost = ((UdpPortInfo) comm.getPortInfo()).getRemoteAddress();
                int port = ((UdpPortInfo) comm.getPortInfo()).getReceivePort();
                values += ("\t" + sHost + "\t" + port + "\t" + comm.getPortInfo().getPortType() + "\t");
            } else
                values += ("\t" + "null" + "\t" + "null" + "\t" + "null" + "\t");
            if (comm.getProtocolName() != null) {
                //sProtocol;
                values += (comm.getProtocolName() + "\t");
            } else
                values += ("null" + "\t");
            values += comm.getRtuType() + "\n";
            buffer.append(values);
        }
        try {
            Writer w = new OutputStreamWriter(new FileOutputStream(commInfoFile), "UTF8");
            w.write(buffer.toString());
            w.close();
        } catch (IOException e) {
            log.warn(e);
            return false;
        }
        return true;
    }

    /**
     * This method is not efficient.
     * @param unit unit to save
     * @return is successful
     */
    public boolean saveUnit(AbstractUnit unit) {
        if(unit instanceof CommUnit) {
            commUnits.put(unit.getId(), (CommUnit) unit);
        } else if(unit instanceof DefaultUnit)
            units.put(unit.getId(), (DefaultUnit) unit);
        return save();
    }

    public boolean removeUnit(AbstractUnit unit) {
        if (unit instanceof CommUnit) {
            commUnits.remove(unit.getId());
        } else {
            if (unit instanceof IUnitManager) {
                IUnitManager manager = (IUnitManager) unit;
                for (AbstractUnit u : manager.getAllUnits().values())
                    removeUnit(u);
            }
            units.remove(unit.getId());
        }
        return save();
    }

    public List<IUnitManager> getRoots() {
        List<IUnitManager> l = new ArrayList<IUnitManager>();
        for (AbstractUnit u : getUnits(-1))
            l.add((IUnitManager) u);
        return l;
    }

    public List<OriginalByteData> getOrinalDataList(int commId, String tableName) {
        return null;
    }

    public List<AbstractUnit> getUnits(int parentId) {
        List<AbstractUnit> r = new ArrayList<AbstractUnit>();
        for(AbstractUnit unit : units.values())
            if(unit.getParentUnitId() == parentId)
                r.add(unit);
        for(AbstractUnit unit : commUnits.values())
            if(unit.getParentUnitId() == parentId)
                r.add(unit);
        return r;
    }

    private void fillUnitManager() {
        List<DefaultUnit> allUnits = new ArrayList<DefaultUnit>(units.values());
        for(DefaultUnit defaultUnit : units.values()) {
            PersistenceUnit unit = (PersistenceUnit) defaultUnit;
            unit.setAddNew(false);
        }
        for(DefaultUnit defaultUnit : units.values()) {
            PersistenceUnit unit = (PersistenceUnit) defaultUnit;
            for(CommUnit comm : commUnits.values())
                if(comm.getParentUnitId() == unit.getId())
                    unit.addUnit(comm);
            for(DefaultUnit u : allUnits)
                if(u.getParentUnitId() == unit.getId())
                    unit.addUnit(u);
        }
        for(DefaultUnit defaultUnit : units.values()) {
            PersistenceUnit unit = (PersistenceUnit) defaultUnit;
            unit.setAddNew(true);
            unit.setPersistence(this);
        }
    }

    private CommUnit fillCommUnit(String[] content) {
        CommUnit comm = new CommUnit(Integer.parseInt(content[0]), content[2].trim());
        comm.setParentUnitId(Integer.parseInt(content[1]));
        comm.setDigitalPointNum(Integer.parseInt(content[3]));
        comm.setDigitalBitNum(Integer.parseInt(content[4]));
        comm.setAnalogNum(Integer.parseInt(content[5]));
        comm.setElectricNum(Integer.parseInt(content[6]));
        String commType = content[9].trim();//commType is not used for forming port info, 2012.1.19
        String protocolName = content[10].trim();
        comm.setRtuType(Integer.parseInt(content[11]));
        if (protocolName != null && !protocolName.equals("") && !protocolName.equals("null")) {
            comm.setProtocolName(protocolName);
            int connMode = HandlerFactory.getConnMode(comm.getRtuType());
            //if (commType != null && commType.equals(IPortInfo.COMM_TCP) && connMode != -1)
            if(connMode == FesProperties.CONN_MODE_TCP_SERVER || connMode == FesProperties.CONN_MODE_TCP_CLIENT)
                comm.setPortInfo(new TcpPortInfo(content[7].trim(), Integer.parseInt(content[8]), connMode));
            else if(connMode == FesProperties.CONN_MODE_UDP)
                comm.setPortInfo(new UdpPortInfo(Integer.parseInt(content[8]), Integer.parseInt(content[8]), content[7].trim()));
            //todo handle: other type of communicate like serial
        }
        if (comm.getId() > PersistenceUnit.getMaxId())
            PersistenceUnit.setMaxId(comm.getId());
        return comm;
    }

    public AbstractUnit getUnit(int id) {
        return units.get(id);
    }

    public CommUnit getCommUnit(int id) {
        return commUnits.get(id);
    }


    /* (non-Javadoc)
      * @see com.ht.aems.fesmodel.FesPersistence#getCommUnitByName(java.lang.String)
      */
    public List<CommUnit> getCommUnitByName(String name) {
        List<CommUnit> r = new ArrayList<CommUnit>();
        for(CommUnit comm : commUnits.values())
            if(comm.getName().equals(name))
                r.add(comm);
        return r;
    }


    public List<CommUnit> getCommUnitByType(int type) {
        List<CommUnit> r = new ArrayList<CommUnit>();
        for(CommUnit comm : commUnits.values())
            if(comm.getRtuType() == type)
                r.add(comm);
        return r;
    }
}
